@file:Suppress("PackageName", "LocalVariableName")

package com.evendai.sipdemo.util

import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.NetworkCapabilities
import android.os.BatteryManager
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import com.evendai.loglibrary.Timber
import com.evendai.sipdemo.*
import org.doubango.ngn.media.NgnProxyAudioConsumer
import org.doubango.ngn.sip.NgnAVSession
import java.io.File
import java.net.NetworkInterface
import java.util.zip.Deflater
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream


@Suppress("unused")
object Utils {

    private val TAG: String = Utils::class.java.simpleName

    fun isValidBallId(ballId: String?): Boolean {
        if (ballId.isNullOrBlank()) return false

        var isValid = false
        try {
            /*val intBallId = */Integer.parseInt(ballId)
            if (ballId.length == 5 && ballId.first() == '3'/* && intBallId > 30300*/) isValid = true
        } catch (e: Exception) {  }
        return isValid
    }


    @Suppress("unused")
    fun printAllInterfaces() {
        object : Thread() {
            @Suppress("SpellCheckingInspection")
            override fun run() {
                try {
                    Timber.i("----------------------------------开始--------------------------------------")
                    val enNetworkInterface = NetworkInterface.getNetworkInterfaces() //获取本机所有的网络接口
                    while (enNetworkInterface.hasMoreElements()) {  //判断 Enumeration 对象中是否还有数据
                        val ni = enNetworkInterface.nextElement()   //获取 Enumeration 对象中的下一个数据
                        Timber.i("interfaceName = " + ni.name + ", displayName = " + ni.displayName
                                + ", isVirtual() = " + ni.isVirtual + ", isUp() = " + ni.isUp + ", isLoopback() = " + ni.isLoopback + ", supportsMulticast() = "
                                + ni.supportsMulticast() + ", isPointToPoint() = " + ni.isPointToPoint)
                        val enInetAddress = ni.inetAddresses   //getInetAddresses 方法返回绑定到该网卡的所有的 IP 地址。
                        while (enInetAddress.hasMoreElements()) {
                            val ia = enInetAddress.nextElement()
                            Timber.i("HostName = " + ia.hostName + ", Address = " + ia.hostAddress + ", isLoopbackAddress = " + ia.isLoopbackAddress + ", isLinkLocalAddress = " + ia.isLinkLocalAddress + ", class = " + ia.javaClass.name)
                        }
                    }
                    Timber.i("----------------------------------结束--------------------------------------")
                } catch (e: Exception) {
                    e.printStackTrace()
                }

            }
        }.start()
    }

    /** 重启Android系统 */
    fun rebootAndroidSystem() {
        val powerManager = App.getContext().getSystemService(Context.POWER_SERVICE) as PowerManager
        powerManager.reboot(null) // 参数：重启的原因
    }


    fun printAllMethod(obj: Any) {
        val clazz: Class<*> = if (obj is Class<*>) {
            obj
        } else {
            obj.javaClass
        }
        clazz.methods.forEach { Log.e(TAG, "method: ${it.name} + args.size = ${it.parameterTypes.size}") }
    }



    @Suppress("unused")
    fun printMethodUseStackTrace() {
        Throwable().stackTrace.forEachIndexed { index, stackTraceElement ->
            Timber.d(stackTraceElement.className)//返回类的完全限定名，该类包含由该堆栈跟踪元素所表示的执行点。
            Timber.d(stackTraceElement.fileName)//返回源文件名，该文件包含由该堆栈跟踪元素所表示的执行点。
            Timber.d(stackTraceElement.lineNumber.toString())//返回源行的行号，该行包含由该堆栈该跟踪元素所表示的执行点。
            Timber.d(stackTraceElement.methodName)//返回方法名，此方法包含由该堆栈跟踪元素所表示的执行点。
            Timber.d("-------------第" + index + "级调用-------------------")
        }
    }

    /** 挂断所有联合会议（语音类型的会议） */
    fun hangupAllAudioConference() {
        queryAllAudioConference(::hangupAudioConference)
        StorageUtil.putString(Keys.AUDIO_CONFERENCE_SESSION_IDS, "") // 所有会议都挂断了，就没必要保存了
    }

    /**
     * 查询所有的语音会议
     * @param handleAudioConference 用于处理查询到的语音会议
     */
    private fun queryAllAudioConference(handleAudioConference: (sessionId: Long, number: String) -> Unit) {
        val sessionIdsAndNumbers = StorageUtil.getStringOrNull(Keys.AUDIO_CONFERENCE_SESSION_IDS)
        Timber.e("sessionIdsAndNumbers = $sessionIdsAndNumbers")
        if (sessionIdsAndNumbers.isNullOrBlank() || !sessionIdsAndNumbers.contains("&")) {
            Timber.e("保存的sessionIdsAndNumbers无效: $sessionIdsAndNumbers")
            return
        }

        val idAndNumbers: List<String> = if (sessionIdsAndNumbers.contains("-")) {
            sessionIdsAndNumbers.split("-") // 有多个会议
        } else {
            listOf(sessionIdsAndNumbers) // 只有一个会议
        }

        idAndNumbers.forEach { idAndNumber ->
            if (idAndNumber.isBlank() || !idAndNumber.contains("&")) {
                Timber.e("非法的idAndNumber = $idAndNumber")
                return@forEach // 相当于Java中的break
            }

            val (sessionIdString, number) = idAndNumber.split("&")
            try {
                val sessionId = sessionIdString.toLong()
                handleAudioConference(sessionId, number)
            } catch (e: java.lang.Exception) {
                Timber.e(e,"不能转换为long的id：$sessionIdString")
            }
        }

    }

    /**
     * 挂断指定的音频会议
     * @param sessionId 会话id
     * @param number 会议号码
     */
    @Suppress("MemberVisibilityCanBePrivate")
    fun hangupAudioConference(sessionId: Long, number: String) {
        val ngnAVSession = NgnAVSession.getSession(sessionId)
        Timber.e("ngnAVSession = $ngnAVSession")
        if (ngnAVSession != null) {
            ngnAVSession.hangUpCall()
            Timber.e("挂断了联合会议，number = $number, sessionId = $sessionId")
        }
    }

    fun <T, R : Comparable<R>> sortByDescending(array: Array<out T>, selector: (T) -> R?) {
        array.sortByDescending(selector)
    }


    fun zipFile(srcFile: File): File? {
        try {
            val zipFile = getZipFile(srcFile)
            ZipOutputStream(zipFile.outputStream()).use { zos ->
                zos.setLevel(Deflater.BEST_COMPRESSION)
                val zipEntry = ZipEntry(srcFile.name)
                zipEntry.time = zipFile.lastModified()
                zos.putNextEntry(zipEntry)
                srcFile.inputStream().use { ins ->
                    val array = ByteArray(8192)
                    var len: Int
                    while (ins.read(array).also { len = it } != -1) {
                        zos.write(array, 0, len)
                    }
                }
                zos.closeEntry()
            }
            return zipFile
        } catch (e: java.lang.Exception) {
            Timber.e(e, "压缩文件（${srcFile.absolutePath}）时出现异常")
            return null
        }
    }

    private fun getZipFile(srcFile: File): File {
        val srcFilePath = srcFile.absolutePath
        val index = srcFilePath.lastIndexOf(".")
        val zipFileName = srcFilePath.substring(0, index) + ".zip"
        return File(zipFileName)
    }

    /** 判断是否有可用网络，如果没有任何可用网络，返回true，否则返回false */
    @Suppress("DEPRECATION")
    fun hasActiveNetwork(): Boolean {
        val manager = App.getConnectivityManager()

        // 当关闭“数据连接”开关时，而且没有Wifi，则activeNetwork或activeNetworkInfo为null
        // 当sim卡过期不管有无信号，activeNetwork或activeNetworkInfo为null
        // 只要有sim卡或者连接上wifi，不论是否能连接互联网，都会返回true。
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Timber.i("Android6.0或以上版本网络信息：")
            // Android 6.0或以上
            /* 使用小米手机测试结果：连接上一个没插网线的wifi,NET_CAPABILITY_INTERNET返回为true，NET_CAPABILITY_VALIDATED返回为false,
               这说明NET_CAPABILITY_INTERNET与ActiveNetworkInfo.isConnected的功能一样，只要连上了sim卡或wifi，不管是否能连上互联网，都返回true。
               关闭wifi，使用公司apn卡，NET_CAPABILITY_INTERNET返回为true，NET_CAPABILITY_VALIDATED返回为false,此时是可以连接apn专网的，说明
               NET_CAPABILITY_VALIDATED是访问互联网来验证的，所以在使用APN卡时，不能使用NET_CAPABILITY_VALIDATED来判断网络是否OK。
               而且NET_CAPABILITY_VALIDATED也不是百分之百可靠，在公司的手机上，连接上一个没网的wifi，返回的也是true。
               总结就是：使用NET_CAPABILITY_INTERNET判断就可以了，然后再写代码连接公司服务器，能连接上就认为网络是OK的。
               但是实验的时候，发现公司的手机启动VPN之后，NET_CAPABILITY_INTERNET返回false，而NET_CAPABILITY_VALIDATED返回true，所以
               可以两个一起判断，只要其中一个为true，则结果为true
             */
            val activeNetwork = manager.activeNetwork
            return if (activeNetwork != null) {
                val networkCapabilities = manager.getNetworkCapabilities(activeNetwork)
                val isInternet = networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
                val isValidated = networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false
                val isWifi = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false
                val isCellular = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ?: false
                val isVpn = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ?: false
                Timber.i("isInternet = $isInternet, isValidated = $isValidated, " +
                        "isWifi = $isWifi, isCellular = $isCellular, isVpn = $isVpn")
                Timber.i("networkCapabilities = $networkCapabilities")
                isInternet or isValidated
            } else {
                Timber.i("activeNetwork为null")
                false
            }
        } else {
            // Android 6.0以下
            // 返回有关当前活动的默认数据网络的详细信息。连接后，此网络是传出连接的默认路由。
            // 在启动网络流量之前，应始终检查isConnected（）。如果没有默认网络，则可能返回null。
            // 插着手机卡，连接上Wifi时，Wifi会变成活动网络。
            // 如果wifi网络不正常，而手机卡网络正常，活动网络是否还是wifi呢？经测试，答案：活动网络还是wifi。
            val ani = manager.activeNetworkInfo
            if (ani != null) { // 未指定数据卡时为null(选择用于数据网络的SIM卡)。关闭移动数据网络时也为null
                Timber.i("Android6.0以下版本网络信息：")
                Timber.i("extraInfo = ${ani.extraInfo}") // APN，如：3gwap或"Dazhou2105"（连Wifi时）。报告有关网络状态的额外信息（如果较低的网络层提供了这些信息）。
                Timber.i("reason = ${ani.reason}")  //如：connected。报告尝试建立连接失败的原因（如果有）。
                Timber.i("state = ${ani.state}") // 如：CONNECTED。报告网络的当前粗粒度状态。
                Timber.i("subtypeName = ${ani.subtypeName}")// 如：LTE。返回描述网络子类型的易于理解的名称。
                Timber.i("typeName = ${ani.typeName}")// 如：MOBILE。返回一个易于理解的名称，描述网络的类型，例如“ WIFI”或“ MOBILE”。
                Timber.i("isAvailable = ${ani.isAvailable}")
                Timber.i("isConnected = ${ani.isConnected}")  // 连Wifi时，就算wifi路由没插网线也会返回true。也就是说connected为true不代码可以访问互联网，要用ping的方式。
                Timber.i("isConnectedOrConnecting = ${ani.isConnectedOrConnecting}")
            } else {
                Timber.i("activeNetworkInfo为null")
            }
            return ani?.isConnected ?: false
        }
    }

    /** 获取当前系统电量 */
    fun getCurrentBattery(): Int {
        val batteryManager = App.getContext().getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }

    abstract class EditTextListener : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
    }

    fun requestFloatViewPermission(context: Activity, requestCode: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && !Settings.canDrawOverlays(context)) {
            AlertDialog.Builder(context)
                    .setTitle("提示")
                    .setMessage("本应用需要显示在其他应用的上层权限，请打开此权限")
                    .setPositiveButton("确定") { _, _ ->
                        val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
                        context.startActivityForResult(intent, requestCode)
                    }
                    .setCancelable(false)
                    .show()
        }
    }

    fun initLoginActivityViews(activity: LoginActivity) {
        activity.binding.etDisplayName.setText(PersistentVariables.displayName)
        activity.binding.etSipIp.setText(PersistentVariables.sipIp)
        activity.binding.etSipPort.setText(PersistentVariables.sipPort.toString())
        activity.binding.etSipTransport.setText(PersistentVariables.transport)
        activity.binding.etSipAccount.setText(PersistentVariables.sipAccount)
        activity.binding.etSipPassword.setText(PersistentVariables.sipPassword)
    }

    /** 设置登录界面的EditText的监听器 */
    fun setLoginActivityEditTextInputListener(activity: LoginActivity) {
        activity.binding.etDisplayName.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.displayName = s.trim().toString()
            }
        })
        activity.binding.etSipIp.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.sipIp = s.trim().toString()
            }
        })
        activity.binding.etSipPort.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                val port = s.trim().toString()
                PersistentVariables.sipPort = if (port.isBlank()) 0 else port.toInt()
            }
        })
        activity.binding.etSipTransport.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.transport = s.trim().toString()
            }
        })
        activity.binding.etSipAccount.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.sipAccount = s.trim().toString()
            }
        })
        activity.binding.etSipPassword.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.sipPassword = s.trim().toString()
            }
        })
    }

    /** 初始化主界面的View */
    fun initMainActivityViews(activity: MainActivity) {
        activity.binding.etSipNumber.setText(PersistentVariables.outgoingNumber)
        activity.binding.etAudioMeetingNumber.setText(PersistentVariables.audioMeetingNumber)
        activity.binding.etVideoMeetingNumber.setText(PersistentVariables.videoMeetingNumber)
        activity.binding.etAudioMeetingPrefix.setText(PersistentVariables.audioMeetingPrefix)
        activity.binding.etVideoMeetingPrefix.setText(PersistentVariables.videoMeetingPrefix)
        activity.binding.etMainVideoMeetingPrefix.setText(PersistentVariables.mainVideoMeetingPrefix)
        activity.binding.etSipMessage.setText(PersistentVariables.sipMessage)
    }

    /** 设置主界面的EditText的监听器 */
    fun setMainActivityEditTextInputListener(activity: MainActivity) {
        activity.binding.etSipNumber.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.outgoingNumber = s.trim().toString()
                }
            })
        activity.binding.etAudioMeetingNumber.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.audioMeetingNumber = s.trim().toString()
                }
            })
        activity.binding.etVideoMeetingNumber.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.videoMeetingNumber = s.trim().toString()
                }
            })
        activity.binding.etAudioMeetingPrefix.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.audioMeetingPrefix = s.trim().toString()
                }
            })
        activity.binding.etVideoMeetingPrefix.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.videoMeetingPrefix = s.trim().toString()
                }
            })
        activity.binding.etMainVideoMeetingPrefix.addTextChangedListener(object: EditTextListener() {
                override fun afterTextChanged(s: Editable) {
                    PersistentVariables.mainVideoMeetingPrefix = s.trim().toString()
                }
            })

        activity.binding.etSipMessage.addTextChangedListener(object: EditTextListener() {
            override fun afterTextChanged(s: Editable) {
                PersistentVariables.sipMessage = s.trim().toString()
            }
        })
    }

}